/*    file: CRtpPsMediaCaster.cpp
 *    desc:
 * 
 * created: 2018-03-14
 *  author: chuanjiang.zh@qq.com
 * company: 
 */

#include "CRtpPsMediaCaster.h"
#include "CUdpMediaCaster.h"
#include "CTcpMediaCaster.h"
#include "PsCaster.h"
#include "CLog.h"
#include "Ffmpeg.h"
#include "TFileUtil.h"

namespace av
{


CRtpPsMediaCaster::CRtpPsMediaCaster(int protocol):
	m_protocol(protocol),
	m_videoLastPts(AV_NOPTS_VALUE),
	m_audioLastPts(AV_NOPTS_VALUE),
	m_id()
{
	if (protocol == PSCASTER_PROTOCOL_RTP_TCP)
	{
        CTcpMediaCaster* tcpMediaCaster = new CTcpMediaCaster();
        tcpMediaCaster->setSink(this);
        m_caster.reset(tcpMediaCaster);
	}
	else
	{
		m_caster.reset(new CUdpMediaCaster());
	}

	m_muxer.reset(new PSMuxer());
}

CRtpPsMediaCaster::~CRtpPsMediaCaster()
{
	close();
}

int CRtpPsMediaCaster::open(const std::string& ip, uint16_t port, const MediaFormat& fmt)
{
	int rc = m_caster->open(ip, port, MEDIA_CODEC_PS, fmt.m_clockRate, m_payload, m_ssrc);

	if (!m_muxer->setCodec(fmt.m_codec, fmt.m_audioCodec))
	{
		return EINVAL;
	}

	m_format = fmt;

	m_caster->onMediaFormat(m_format);

	m_propDetector.setCodec(fmt.m_codec);
	m_naluJoiner.reset(createNaluJoiner(fmt.m_codec));

	return rc;
}

void CRtpPsMediaCaster::close()
{
	m_caster->close();

	m_propDetector.clear();
}

bool CRtpPsMediaCaster::isOpen()
{
	return m_caster->isOpen();
}

void CRtpPsMediaCaster::setID(int id)
{
	m_id = id;

	m_caster->setID(id);
}

int CRtpPsMediaCaster::getID()
{
	return m_caster->getID();
}

bool CRtpPsMediaCaster::addTarget(const std::string& ip, uint16_t port)
{
	return m_caster->addTarget(ip, port);
}

void CRtpPsMediaCaster::removeTarget(const std::string& ip, uint16_t port)
{
	m_caster->removeTarget(ip, port);
}

size_t CRtpPsMediaCaster::getTargetCount()
{
	return m_caster->getTargetCount();
}

void CRtpPsMediaCaster::removeAllTarget()
{
	m_caster->removeAllTarget();
}

bool CRtpPsMediaCaster::write(av::MediaPacketPtr& pkt)
{
	return write(pkt->type, pkt->data, pkt->size, pkt->pts, pkt->duration, pkt->flags);
}


void CRtpPsMediaCaster::setPayload(uint8_t payload)
{
	m_payload = payload;
}

void CRtpPsMediaCaster::setSsrc(uint32_t ssrc)
{
	m_ssrc = ssrc;
}


bool CRtpPsMediaCaster::write(int type, uint8_t* data, int size, int64_t pts, int duration, int flags)
{
	comn::AutoCritSec lock(m_cs);

	if (type == MEDIA_TYPE_VIDEO)
	{
		if (m_format.m_clockRate > 0 && m_format.m_clockRate != 90000)
		{
			pts = pts * 90000 / m_format.m_clockRate;
		}

		if (m_videoLastPts > 0)
		{
			if (m_videoLastPts > pts)
			{
				CLog::warning("(%d) video pts is invalid. last:%lld, now:%lld\n", m_id, m_videoLastPts, pts);
			}
		}

		bool isKeyFrame = false;
		int naluType = 0;
		m_propDetector.detect(data, size, isKeyFrame, naluType);

		flags |= isKeyFrame ? MEDIA_FLAG_KEY : 0;

		MPacket inPkt;
		inPkt.type = MEDIA_TYPE_VIDEO;
		inPkt.data = data;
		inPkt.size = size;
		inPkt.pts = pts;
		inPkt.flags = flags;
		inPkt.duration = 0;

		MPacket outPkt;
		memset(&outPkt, 0, sizeof(outPkt));

		if (m_naluJoiner->write(inPkt, naluType, outPkt))
		{
			bool keyFrame = (outPkt.flags & MEDIA_FLAG_KEY) != 0;
			uint8_t* outData = 0;
			int outSize = 0;
			m_muxer->writeVideo(outPkt.data, outPkt.size, keyFrame,
				outPkt.pts, outData, outSize);
			if (outSize > 0)
			{
				writePacket(outData, outSize, outPkt.pts, outPkt.flags);
			}
		}
		else
		{
			// pass
		}

		m_videoLastPts = pts;

		//CLog::debug("write video. size:%d. pts:%lld, duration:%d.    elapse:%d\n",
		//				size, pts, duration, (int)elapse);

		return true;
	}
	else
	{
		if (m_format.m_audioRate > 0 && m_format.m_audioRate != 90000)
		{
			pts = pts * 90000 / m_format.m_audioRate;
		}

		m_audioLastPts = pts;

		uint8_t* outData = 0;
		int outSize = 0;
		m_muxer->writeAudio(data, size, pts, outData, outSize);
		if (outSize > 0)
		{
			writePacket(outData, outSize, pts, flags);
		}

		return true;
	}
}

void CRtpPsMediaCaster::writePacket(uint8_t* data, int size, int64_t pts, int flags)
{
	av::MediaPacketPtr pkt(new av::MediaPacket());
	pkt->type = MEDIA_TYPE_DATA;
	pkt->write(data, size);
	pkt->pts = pts;
	pkt->flags = flags;

	m_caster->onMediaPacket(pkt);

	//comn::FileUtil::write(data, size, "out.ps", true);
}

void CRtpPsMediaCaster::onTcpEvent(void* obj, int event)
{
    PsCasterEvent casterEvent = PsCasterEvent();
    casterEvent.handle = getID();
    casterEvent.type = event + (CASTER_EVENT_CONNECTING - 1);
    fireCasterEvent(casterEvent);
}



}